# -*- coding: utf-8 -*-

from gamestate import *


class GameStateStatistics:
    """
        This class can count
        1) average guessing time for round, for game
        2) dispersion of guessing time
        3) min\max guessing time for round\game
    """

    def __init__(self, game_state):
        self.game_state = game_state

    @staticmethod
    def create_word_list(game_round):
        """
        This method can create list of GUESSED gamestate.WGR from
        game_round.words.
        It can normalize guessing time
        (from [1, 9, 12, 16, 28] (time left from start of round)
        to [1, 8, 3, 4, 12] (time left from last event (GUESS,
        FAIL, etc))

        Args:
            game_round: gamestate.GameRound
        Returns:
            list of gamestate.WordGuessResult
        """
        out_list = []
        guessing_time = 0
        for word_guess_result in game_round.words:
            if word_guess_result.result == WordGuessResult.GUESS:
                out_list.append(WordGuessResult(
                    word_guess_result.word,
                    word_guess_result.result,
                    word_guess_result.time - guessing_time
                ))
            guessing_time = word_guess_result.time
        return out_list

    @staticmethod
    def get_average_guessing_time_per_round(game_round):
        """
        This method can count average value for round.
        Args:
            game_round: gamestate.GameRound
        Returns:
            (float)
        """
        guessing_time = 0
        times = []
        for word in GameStateStatistics.create_word_list(game_round):
            guessing_time += word.time
            times.append(word.time)
        word_count = len(game_round.words)
        return 0 if not word_count else float(guessing_time) / word_count

    @staticmethod
    def get_average_guessing_time(rounds, pair=None):
        """
        This method can count average value for game.
        Args:
            game_rounds: list of gamestate.GameRound
            pair: pair of string (names of players)
        Returns:
            (float)
        """
        guessing_time = 0
        word_count = 0
        for round in rounds:
            if pair:
                player1, player2 = round.players_pair
                a, b = pair
                sorted_pair = min(a, b), max(a, b)
                sorted_round_pair = \
                    min(player1.name, player2.name),\
                    max(player1.name, player2.name)
            if not pair or sorted_pair == sorted_round_pair:
                word_count += len(round.words)
                for word in GameStateStatistics.create_word_list(round):
                    guessing_time += word.time
        return 0 if not word_count else float(guessing_time) / word_count

    @staticmethod
    def get_dispersion_per_round(game_round):
        """
        This method can count dispersion for round.
        Args:
            game_round: gamestate.GameRound
        Returns:
            (float)
        """
        func = {}
        for word in GameStateStatistics.create_word_list(game_round):
            if word.time in func:
                func[word.time] += 1
            else:
                func[word.time] = 1
        cnt = 0
        time_cnt = len(func)
        expected = GameStateStatistics.\
            get_average_guessing_time_per_round(game_round)
        for i in func:
            cnt += float(func[i]) / time_cnt * (i - expected)**2
        return cnt

    @staticmethod
    def get_dispersion(game_rounds, pair=None):
        """
        This method can count average value for game.
        Args:
            game_rounds: list of gamestate.GameRound
            pair: pair of string (names of players)
        Returns:
            (float)
        """
        func = {}
        for game_round in game_rounds:
            if pair:
                player1, player2 = round.players_pair
                a, b = pair
                sorted_pair = min(a, b), max(a, b)
                sorted_round_pair = \
                    min(player1.name, player2.name),\
                    max(player1.name, player2.name)
            if not pair or sorted_pair == sorted_round_pair:
                for word in GameStateStatistics.create_word_list(game_round):
                    if word.time in func:
                        func[word.time] += 1
                    else:
                        func[word.time] = 1
        cnt = 0
        time_cnt = len(func)
        expected = GameStateStatistics.\
            get_average_guessing_time_per_round(game_round)
        for i in func:
            cnt += float(func[i]) / time_cnt * (i - expected)**2
        return cnt

    @staticmethod
    def get_min_guessing_time_in_round(game_round):
        """
        This method can get min guessing time for round.
        Args:
            game_round: gamestate.GameRound
        Returns:
            (int)
        """
        res = 10**9
        for word in GameStateStatistics.create_word_list(game_round):
            res = min(res, word.time)
        return 0 if res == 10**9 else res

    @staticmethod
    def get_min_guessing_time(game_rounds):
        """
        This method can get min guessing time for game.
        Args:
            game_rounds: list of gamestate.GameRound
        Returns:
            (float)
        """
        res = 10**9
        for round in game_rounds:
            res = min(res,
                      GameStateStatistics.
                      get_min_guessing_time_in_round(round))
        return 0 if res == 10**9 else res

    @staticmethod
    def get_max_guessing_time_in_round(game_round):
        """
        This method can get max guessing time for round.
        Args:
            game_round: gamestate.GameRound
        Returns:
            (int)
        """
        res = 0
        for word in GameStateStatistics.create_word_list(game_round):
            res = max(res, word.time)
        return res

    @staticmethod
    def get_max_guessing_time(game_rounds):
        """
        This method can get max guessing time for game.
        Args:
            game_rounds: list of gamestate.GameRound
        Returns:
            (float)
        """
        res = 0
        for round in game_rounds:
            res = max(res,
                      GameStateStatistics.
                      get_max_guessing_time_in_round(round))
        return res
